
#include <cstdio>
#include <BAudioInput>
#include "member_BAudioInput.h"
#include "IGlobal.h"

#define member					(*(member_BAudioInput*)_ptr)
#define member_allocate()		_ptr = new member_BAudioInput(this)
#define member_release()		delete (member_BAudioInput*)_ptr

using namespace BWE;

BAudioInput::BAudioInput()
{
	member_allocate();
	app_audioInputs.append(this);
}
BAudioInput::~BAudioInput()
{
	member_release();
	app_audioInputs.remove(this);
}

bool BAudioInput::setFormat(Format format)
{
	if (member.format != format)
	{
		member.format = format;
	}
	return true;
}
Format BAudioInput::format() const
{
	return member.format;
}

bool BAudioInput::setChannelCount(int channelCount)
{
	if (member.channelCount != channelCount)
	{
		member.channelCount = channelCount;
	}
	return true;
}
int BAudioInput::channelCount() const
{
	return member.channelCount;
}

bool BAudioInput::setSampleRate(int sampleRate)
{
	if (member.sampleRate != sampleRate)
	{
		member.sampleRate = sampleRate;
	}
	return true;
}
int BAudioInput::sampleRate() const
{
	return member.sampleRate;
}

BAudio::State BAudioInput::state() const
{
	return member.state;
}

#ifdef linux
int BAudioInput::DeviceNames(BStringList& namelst)
{
	namelst.clear();
	namelst.append("/dev/dsp");
	return namelst.size();
}

bool BAudioInput::open()
{
	char* pathname = 0;
	int nSampleRate = 0;
	int nChannels = 2;
	int fmt = 0;

	int mix_fd, status, arg;
	member.dsp_fd = ::open("/dev/dsp", O_RDWR);   /*open dsp*/
	if (member.dsp_fd < 0)
	{
		puts("open dsp failed!");
		return false;
	}
	arg = nSampleRate;
	status = ioctl(member.dsp_fd, SOUND_PCM_WRITE_RATE, &arg); /*set samplerate*/
	if (status < 0)
	{
		::close(member.dsp_fd);
		puts("samplerate status failed");
		return false;
	}
	if (arg != nSampleRate)
	{
		::close(member.dsp_fd);
		puts("set samplerate failed");
		return false;
	}
	arg = nChannels;  /*set channels*/
	status = ioctl(member.dsp_fd, SOUND_PCM_WRITE_CHANNELS, &arg);
	if (status < 0)
	{
		::close(member.dsp_fd);
		puts("Channels status failed");
		return false;
	}
	if (arg != nChannels)
	{
		::close(member.dsp_fd);
		puts("set channels failed");
		return false;
	}
	arg = fmt; /*set bit fmt*/
	status = ioctl(member.dsp_fd, SOUND_PCM_WRITE_BITS, &arg);
	if (status < 0)
	{
		::close(member.dsp_fd);
		puts("FMT status failed");
		return false;
	}
	if (arg != fmt)
	{
		::close(member.dsp_fd);
		puts("set fmt failed");
		return false;
	}
	member.file_fd = fopen(pathname, "r");
	if (member.file_fd == NULL)
	{
		::close(member.dsp_fd);
		puts("opem filed failed");
		return false;
	}
	return true;
}
bool BAudioInput::close()
{
	if (member.dsp_fd)
	{
		::close(member.dsp_fd);
		fclose(member.file_fd);
		member.dsp_fd = 0;
		member.file_fd = 0;
		return true;
	}
	return false;
}

bool BAudioInput::start()
{
	if (member.dirtyFormat)
	{
		member.initFormat();
	}
	int num = 3 * member.channelCount * member.sampleRate * member.fmtlen / 8;
	int get_num;
	char buf[num];
	while (feof(member.file_fd) == 0)
	{
		get_num = fread(buf, 1, num, member.file_fd);
		write(member.dsp_fd, buf, get_num);
		if (get_num != num)
		{
			::close(member.dsp_fd);
			fclose(member.file_fd);
			member.dsp_fd = 0;
			member.file_fd = 0;
			return 0;
		}
	}
	return true;
}
bool BAudioInput::pause()
{
	 return true;
}
bool BAudioInput::stop()
{
	 return true;
}

#endif

#ifdef _WIN32
void CALLBACK WaveInProc(HWAVEIN hWaveIn, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
{
	if (WIM_DATA != uMsg)
		return;

	BAudioInput* audio = (BAudioInput*)dwInstance;
	member_BAudioInput* mem = audioinput_member(audio);
	if (mem->hWaveIn != hWaveIn)
	{
		return;
	}
	PWAVEHDR whd = (PWAVEHDR)dwParam1;
	switch (uMsg)
	{
	case WIM_OPEN:
		break;
	case WIM_DATA:
	{
		LPWAVEHDR wavHdr = (LPWAVEHDR)dwParam1;
		int length = wavHdr->dwBufferLength;
		int size = wavHdr->dwBytesRecorded;
		char* data = wavHdr->lpData;
		EnterCriticalSection(&mem->bufferSection);
		if (mem->buffer.size() < length * 10)
		{
			mem->buffer.append((BByte*)data, size);
			printf("audio input %d.\n", size);
		}
		LeaveCriticalSection(&mem->bufferSection);
		waveInAddBuffer(hWaveIn, wavHdr, sizeof(WAVEHDR));
		break;
	}
	case WIM_CLOSE:
		break;
	default:
		break;
	}
}

int BAudioInput::DeviceNames(BStringList& namelst)
{
	char cstr[256];
	namelst.clear();
	int num = waveInGetNumDevs();
	for (int i = 0; i < num; i++)
	{
		WAVEINCAPS wic;
		waveInGetDevCaps(i, &wic, sizeof(WAVEINCAPS));
		bWcsToMbs(wic.szPname, cstr);
		namelst.append(cstr);
	}
	return namelst.size();
}

bool BAudioInput::open()
{
	if (member.hWaveIn)
		return false;
	member.initFormat();
	MMRESULT result = waveInOpen(&member.hWaveIn, WAVE_MAPPER, &member.wavform, (DWORD_PTR)WaveInProc, (DWORD_PTR)this, CALLBACK_FUNCTION);
	switch (result)
	{
	case MMSYSERR_NOERROR:
		member.state = BAudio::State_Opened;
		break;
	case MMSYSERR_ALLOCATED:
		puts("Specified resource is already allocated.");
		return false;
	case MMSYSERR_BADDEVICEID:
		puts("Specified device identifier is out of range.");
		return false;
	case MMSYSERR_NODRIVER:
		puts("No device driver is present.");
		return false;
	case MMSYSERR_NOMEM:
		puts("Unable to allocate or lock memory.");
		return false;
	case WAVERR_BADFORMAT:
		puts("Attempted to open with an unsupported waveform - audio format.");
		return false;
	default:
		return false;
	}
	for (int i = 0; i < 10; i++)
	{
		waveInPrepareHeader(member.hWaveIn, &member.wavhdr[i], sizeof(WAVEHDR));
		waveInAddBuffer(member.hWaveIn, &member.wavhdr[i], sizeof(WAVEHDR));
	}
	return true;
}
bool BAudioInput::close()
{
	if (member.hWaveIn)
	{
		waveInReset(member.hWaveIn);
		for (int i = 0; i < 10; i++)
		{
			waveInUnprepareHeader(member.hWaveIn, &member.wavhdr[i], sizeof(WAVEHDR));
			delete member.wavhdr[i].lpData;
			member.wavhdr[i].lpData = 0;
		}
		waveInClose(member.hWaveIn);
		member.hWaveIn = 0;
		return true;
	}
	return false;
}

bool BAudioInput::start()
{
	if (!member.hWaveIn)
		return false;
	MMRESULT result = waveInStart(member.hWaveIn);
	switch(result)
	{
	case MMSYSERR_NOERROR:
		puts("recording..........");
		member.state = BAudio::State_Started;
		return true;
	case MMSYSERR_INVALHANDLE:
		puts("Specified device handle is invalid.");
		break;
	case MMSYSERR_NODRIVER:
		puts("No device driver is present.");
		break;
	case MMSYSERR_NOMEM:
		puts("Unabled to allocate or lock memory.");
		break;
	}
	return false;
}
bool BAudioInput::stop()
{
	if (!member.hWaveIn)
		return false;
	MMRESULT result = waveInStop(member.hWaveIn);
	switch(result)
	{
	case MMSYSERR_NOERROR:
		puts("stop recording..........");
		member.state = BAudio::State_Opened;
		return true;
	case MMSYSERR_INVALHANDLE:
		puts("Specified device handle is invalid.");
		break;
	case MMSYSERR_NODRIVER:
		puts("No device driver is present.");
		break;
	case MMSYSERR_NOMEM:
		puts("Unabled to allocate or lock memory.");
		break;
	}
	return false;
}

#endif
